Skip to content

Tiff: Support decoding f16 images - #3015

Draft
RunDevelopment wants to merge 5 commits into
image-rs:mainfrom
RunDevelopment:tiff-f16
Draft

Tiff: Support decoding f16 images#3015
RunDevelopment wants to merge 5 commits into
image-rs:mainfrom
RunDevelopment:tiff-f16

Conversation

@RunDevelopment

Copy link
Copy Markdown
Member

Resolves #2795
Based on #3014

Changes:

  • Tiff decoder now decodes Gray/Rgb/Rgba f16 images by converting them to f32.
  • Added ExtendedColorType::{L16F, La16F, Rgb16F, Rgba16F}.
  • Fixed a bug in testing infra

I added support for decoding f16 TIFF images by converting them to f32. This is a lossless conversion, if a bit inefficient. Importantly, this does not add new dependencies. I used the methods on half::f16 exposed by the tiff crate to avoid adding half to our direct dependencies.

This correctly decodes the f16 test images from image-rs/image-tiff#257. (Interestingly, a lot of programs don't seem to decode f16 correctly and interpret it as u16. From the programs I tested, only GIMP and Photoshop decode them correctly.)

TODO:

  • I left a TODO on ExtendedColorType, because I probably messed up the order of variants, which breaks serde backwards compat.
  • The f16 -> f32 conversion itself is less efficient than necessary. The half crate has optimized methods for converting a slice of f16s to a slice of f32s, but that requires taking half as a direct dependency. We need to decide whether we want that.

@mhils mhils left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for this PR! I was about to send one with pretty much the same changes, because I somehow missed this one initially. Just leaving minor comments on where we diverged, feel free to disregard them all. 😃

Comment thread src/codecs/tiff.rs
Comment on lines +551 to +553
for (half, out) in v.iter().zip(buf.as_chunks_mut::<4>().0.iter_mut()) {
*out = half.to_f32().to_ne_bytes();
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe a bit more intuitive:

Suggested change
for (half, out) in v.iter().zip(buf.as_chunks_mut::<4>().0.iter_mut()) {
*out = half.to_f32().to_ne_bytes();
}
for (out, f) in bytemuck::cast_slice_mut::<u8, f32>(buf).iter_mut().zip(v) {
*out = f.to_f32();
}

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is wrong, unfortunately. A slice of f32 must have an alignment requirement of 4, but the slice of u8 we have doesn't guarantee that. So bytemuck::cast_slice_mut will panic if the slice doesn't happen to be aligned to a multiple of 4. Users are allowed to give us slices of any alignment, so we have to be careful.

Comment thread src/codecs/tiff.rs Outdated
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Support decoding f16 TIFF images following the tiff crate

2 participants